/******************************************************************************
 * Student Name: Ofri Keidar
 * Exercise Name: Ex2
 * File Description: Class Menu's implementation
 ******************************************************************************/

#include <iostream>
#include "Menu.h"
using namespace std;
#define FIRST_CASE 1 // used to initialize enum type's first value
#define NUM_OF_RESULTS 2 // number of results after trying to add a student to an existing course- True or False

/***************************************************************************************
 * function name: Menu()
 * The Input: None
 * The output: None
 * The Function operation: Allocates new student linked list, lecturer linked list and
 * 						   course linked list
 ***************************************************************************************/
Menu::Menu() {

	m_studentList = new StudentLinkedList(); // allocate student list
	m_lecturerList = new LecturerLinkedList(); // allocate lecturer list
	m_courseLinkedList = new CourseLinkedList(*m_studentList, *m_lecturerList); // allocate course list with references to student and lecturer lists
}

/***************************************************************************************
 * function name: ~Menu()
 * The Input: None
 * The output: None
 * The Function operation: Deletes lists
 ***************************************************************************************/
Menu::~Menu() {
	// free allocated memory
	delete m_studentList;
	delete m_lecturerList;
	delete m_courseLinkedList;
}

/***************************************************************************************
 * function name: studentMenu()
 * The Input: None
 * The output: None
 * The Function operation: Gets user option and performs given option. Stays in menu
 * 						   as long as exit to main menu option was not given. For each
 * 						   choice use suitable method
 ***************************************************************************************/
void Menu::studentMenu() {

	enum Options {
		ADD_NEW_STUDENT = FIRST_CASE,
		SET_STUDENT_NAME,
		PRINT_STUDENT_LIST,
		EXIT_TO_MAIN_MENU
	}; // menu's option values

	bool keepRunning = true; // flag to stay in loop
	short userOption;
	while (keepRunning) {

		cin >> userOption; // get user's option
		switch (userOption) { // perform user's option

			case ADD_NEW_STUDENT: {

				int id;
				string name;
				cin >> id >> name; // get id and name
				addStudent(id, name); // check if a student with given values can be added
				break;
			}

			case SET_STUDENT_NAME: {

				int id;
				string newName;
				cin >> id >> newName; // get id of student to change and new name
				setStudentName(id, newName); // set new name
				break;
			}

			case PRINT_STUDENT_LIST: {

				printStudentList(); // print entire student list
				cout << endl;
				break;
			}

			case EXIT_TO_MAIN_MENU: {
				keepRunning = false; // set keep running flag as false to terminate loop
				break;
			}

				default: { // default case, do nothing
				break;
			}

		} // switch (userOption)
	} // while (keepRunning)

}

/***************************************************************************************
 * function name: lecturerMenu()
 * The Input: None
 * The output: None
 * The Function operation: Gets user option and performs given option. Stays in menu
 * 						   as long as exit to main menu option was not given. For each
 * 						   choice use suitable method
 ***************************************************************************************/
void Menu::lecturerMenu() {

	enum Options {
			ADD_NEW_LECTURER = FIRST_CASE,
			SET_LECTURER_NAME,
			PRINT_LECTURER_LIST,
			EXIT_TO_MAIN_MENU
		}; // menu's option values

		bool keepRunning = true; // flag to stay in loop
		short userOption;
		while (keepRunning) {

			cin >> userOption; // get user's option
			switch (userOption) { // perform user's option

				case ADD_NEW_LECTURER: {

					int id;
					string name;
					cin >> id >> name; // get id and name
					addLecturer(id, name); // check if a lecturer with given values can be added
					break;
				}

				case SET_LECTURER_NAME: {

					int id;
					string newName;
					cin >> id >> newName; // get id of lecturer to change and new name
					setLecturerName(id, newName); // set new name
					break;
				}

				case PRINT_LECTURER_LIST: {

					printLecturerList(); // print entire lecturer list
					cout << endl;
					break;
				}

				case EXIT_TO_MAIN_MENU: {
					keepRunning = false; // set keep running flag as false to terminate loop
					break;
				}

				default: { // default case, do nothing
					break;
				}
			} // switch (userOption)
		} // while (keepRunning)
}


/***************************************************************************************
 * function name: lecturerMenu()
 * The Input: None
 * The output: None
 * The Function operation: Gets user option and performs given option. Stays in menu
 * 						   as long as exit to main menu option was not given. For each
 * 						   choice use suitable method
 ***************************************************************************************/
void Menu::courseMenu() {
	enum Options {
			ADD_NEW_COURSE = FIRST_CASE,
			ADD_STUDENTS_TO_COURSE,
			PRINT_COURSE_DETAILS,
			PRINT_COURSE_LIST,
			EXIT_TO_MAIN_MENU
		}; // menu's option values

	bool keepRunning = true; // flag to stay in loop
	while (keepRunning) {

		short userOption;
		cin >> userOption; // get user's option

		switch (userOption) {

			case ADD_NEW_COURSE: {

				int courseNumber, lecturerID;
				cin >> courseNumber >> lecturerID; // get new course's number and lecturer id
				addCourse(courseNumber, lecturerID); // check if a course with given number and lecturer id can be added
				break;
			}

			case ADD_STUDENTS_TO_COURSE: {

				// get how many students to add
				int courseNumber, numOfStudents;
				cin >> courseNumber >> numOfStudents;

				// allocate an array consisting given id's and adding result for each one
				int** idList = new int*[numOfStudents];
				for(int i = 0; i < numOfStudents; ++i) {
					idList[i] = new int[NUM_OF_RESULTS];
				}

				// write given ID's in the array - first column for ID, second for adding results
				for (int i = 0; i < numOfStudents; ++i) {
					cin >> idList[i][0];
				}

				// print students who could not be added to course (if there are any)
				addStudentsToCourse(courseNumber, numOfStudents, idList);
				//delete allocated memory for idList
				for (int i = 0; i < numOfStudents; ++i) {
					delete[] idList[i]; // delete columns
				}
				delete[] idList; // delete rows
				break;
			} //case ADD_STUDENTS_TO_COURSE

			case PRINT_COURSE_DETAILS: {

				int courseNumber;
				cin >> courseNumber; // get number of course to print
				// check if course exists
				Course* courseToPrint = m_courseLinkedList->searchByKey(courseNumber);
				if (courseToPrint == NULL) { // no such course
					cout << "Error9" << endl;
					break;
				}
				// print given course's details
				printCourseDetails(courseToPrint);
				cout << endl;
				break;
			}

			case PRINT_COURSE_LIST: {
				printCourseList(); // print entire course list
				break;
			}

			case EXIT_TO_MAIN_MENU: {
				keepRunning = false; // set keep running flag as false to terminate loop
				break;
			}

			default: { // default case, do nothing
				break;
			}
		} // switch (userOption)
	} //while (keepRunning)

}

/***************************************************************************************
 * function name: start()
 * The Input: None
 * The output: None
 * The Function operation: Gets user option and performs given option. Stays in menu
 * 						   as long as exit to main menu option was not given. For each
 * 						   choice use suitable method
 ***************************************************************************************/
void Menu::start() {

	enum Options {
		STUDENT_MENU = FIRST_CASE,
		LECTURER_MENU,
		COURSE_MENU,
		EXIT_PROGRAM
	}; // menu's option values

	short userOption;
	bool keepRunning = true; // flag to keep looping
	while (keepRunning) {

		cin >> userOption; // get user's option
		switch (userOption) { // perform user's option

			case STUDENT_MENU: {
				studentMenu(); // call student menu method
				break;
			}

			case LECTURER_MENU: {
				lecturerMenu(); // call lecturer menu method
				break;
			}

			case COURSE_MENU: {
				courseMenu(); // call course menu method
				break;
			}

			case EXIT_PROGRAM: {
				keepRunning = false; // set keep running flag as false to terminate loop
				break;
			}

			default: { // default case, do nothing
				break;
			}
		} // switch (userOption)
	} // while (keepRunning)

}

/***************************************************************************************
 * function name: addStudent(const int id, const string name)
 * The Input: Id and name of new student
 * The output: None
 * The Function operation: Creates a student with given values. If returned value from
 * 					       student list's adding method is True- student was added,
 * 					       if False- student was not added- print error message and
 * 					       delete the new student
 ***************************************************************************************/
void Menu::addStudent(const int id, const string name) {

	Student* newStudent = new Student(id, name); // create a student with given values
	bool studentInList = m_studentList->addNode(newStudent);

	if (studentInList) { // student was added
		return;
	}
	// student was not added, print error message and delte new student
	cout << "Error1" << endl;
	delete newStudent;
	return;
}

/***************************************************************************************
* function name: setStudentName(const int id, const string name)
* The Input: Id of student whose name should be change and new name
* The output: None
* The Function operation: Uses student's list searchByKey method to check if given student
* 					      exists. If so, use the student pointer received by searchByKey
* 					      to change name (with student's set name method). Otherwise,
* 					      print error message
***************************************************************************************/
void Menu::setStudentName(const int id, const string name) {

	// check if list has a student with given id
	Student* studentToChange = m_studentList->searchByKey(id);
	if (studentToChange == NULL) { // no such student in list
		cout << "Error2" << endl;
		return;
	}
	// change given student's name
	studentToChange->setStudentName(name);
	return;
}

/***************************************************************************************
* function name: printStudentList()
* The Input: None
* The output: None
* The Function operation: Initialize student list's current node pointer and print
* 					      current student's details. Keep doing so until list's end is
* 					      reached. At the end initialize current node pointer.
***************************************************************************************/
void Menu::printStudentList() {

	m_studentList->initCurrent(); // initialize current node pointer
	bool keepScanning = m_studentList->incrmntCurrent();
	// print each student's details
	cout << "Students:[";
	while (keepScanning != false) { // has not reached list's end yet

		cout << "Student:[ID:" << m_studentList->getCurrentID() << ", name:" << m_studentList->getCurrentName() << "]"; // print current student's details
		keepScanning = m_studentList->incrmntCurrent(); // increment current node pointer
		if (keepScanning != false) {
			cout << ", ";
		}
	}
	cout << "]";
	m_studentList->initCurrent(); // done printing, initialize current node pointer's position
	return;
}

/***************************************************************************************
* function name: printStudentList(StudentLinkedList& listToPrint)
* The Input: Reference to student list to be printed
* The output: None
* The Function operation: Initialize given student list's current node pointer and print
* 					      current student's details. Keep doing so until list's end is
* 					      reached. At the end initialize current node pointer.
***************************************************************************************/
void Menu::printStudentList(StudentLinkedList& listToPrint) {
//void Menu::printStudentList(StudentLinkedList& listToPrint) {

	listToPrint.initCurrent(); // initialize current node pointer
	bool keepScanning = listToPrint.incrmntCurrent();
	// print each student's details
	cout << "Students:[";
	while (keepScanning != false) { // has not reached list's end yet

		cout << "Student:[ID:" << listToPrint.getCurrentID() << ", name:" << listToPrint.getCurrentName() << "]"; // print current student's details
		keepScanning = listToPrint.incrmntCurrent(); // increment current node pointer
		if (keepScanning != false) {
			cout << ", ";
		}
	}
	cout << "]";
	listToPrint.initCurrent(); // done printing, initialize current node pointer's position
	return;
}

/***************************************************************************************
 * function name: addLecturer(const int id, const string name)
 * The Input: Id and name of new lecturer
 * The output: None
 * The Function operation: Creates a lecturer with given values. If returned value from
 * 					       lecturer list's adding method is True- lecturer was added,
 * 					       if False- lecturer was not added- print error message and
 * 					       delete the new lecturer
 ***************************************************************************************/
void Menu::addLecturer(const int id, const string name) {

	Lecturer* newLecturer = new Lecturer(id, name);
	bool lecturerInList = m_lecturerList->addNode(newLecturer);

	if (lecturerInList) { // lecturer is in list
		return;
	}
	// lecturer already exist and was not added, print an error message
	cout << "Error3" << endl;
	delete newLecturer;
	return;
}

/***************************************************************************************
* function name: setLecturerName(const int id, const string name)
* The Input: Id of lecturer whose name should be change and new name
* The output: None
* The Function operation: Uses lecturer's list searchByKey method to check if given lecturer
* 					      exists. If so, use the lecturer pointer received by searchByKey
* 					      to change name (with lecturer's set name method). Otherwise,
* 					      print error message
***************************************************************************************/
void Menu::setLecturerName(const int id, const string name) {

	// check if list has a student with given id
	Lecturer* lecturerToChange = m_lecturerList->searchByKey(id);
	if (lecturerToChange == NULL) { // no such student in list
		cout << "Error4" << endl;
		return;
	}
	// change given student's name
	lecturerToChange->setName(name);
	return;
}

/***************************************************************************************
* function name: printLecturerList()
* The Input: None
* The output: None
* The Function operation: Initialize lecturer list's current node pointer and print
* 					      current lecturer's details. Keep doing so until list's end is
* 					      reached. At the end initialize current node pointer.
***************************************************************************************/
void Menu::printLecturerList() {

	m_lecturerList->initCurrent(); // initialize current node pointer
	bool keepScanning = m_lecturerList->incrmntCurrent();
	// print each student's details
	cout << "Lecturers:[";
	while (keepScanning != false) { // has not reached list's end yet

		cout << "Lecturer:[ID:" << m_lecturerList->getCurrentID() << ", name:" << m_lecturerList->getCurrentName() << "]";
		keepScanning = m_lecturerList->incrmntCurrent(); // increment current node pointer
		if (keepScanning != false) {
			cout << ", ";
		}
	}
	cout << "]";
	m_lecturerList->initCurrent(); // done printing, initialize current node pointer's position

}

/***************************************************************************************
* function name: addCourse(const int courseNumber, const int lecturerID)
* The Input: New course's number and lecturer's id
* The output: None
* The Function operation: Uses course list's method addCourse to add the course.
* 						  Print suitable error message according to returned value
***************************************************************************************/
void Menu::addCourse(const int courseNumber, const int lecturerID) {

	// check if a course with given values can be added
	CourseLinkedList::AddCourseRetVals addResult = m_courseLinkedList->addCourse(courseNumber, lecturerID);
	if (addResult == CourseLinkedList::COURSE_EXISTS) { // course already exists
		cout << "Error5" << endl;
		return;
	}

	if (addResult == CourseLinkedList::NO_SUCH_LECTURER) { // lecturer does not exist
		cout << "Error6" << endl;
		return;
	}

	// course does not exist but lecturer does exist, course was added to list
	return;
}

/***************************************************************************************
* function name: addStudentsToCourse(const int courseNumber, const int numOfStudents, int** idList)
* The Input: Number of course to add students to, number of students to add and their id's
* The output: Number of students who were not added
* The Function operation: Uses course list's add students to course method returned value
* 					      to indicate if an error occurred (NUMBER_ERROR_VAL) or how many
* 					      students were not added.
* 					      If returned value is not NUMBER_ERROR_VAL, scan idList and print
* 					      each id which was not added- these id's have False in their
* 					      second column in id List (used for adding result)
***************************************************************************************/
void Menu::addStudentsToCourse(const int courseNumber, const int numOfStudents, int** idList) {

	// print students who could not be added to course (if there are any)
	int mismatchCtr = m_courseLinkedList->addStudentsToCourse(courseNumber, numOfStudents, idList);

	if (mismatchCtr == NUMBER_ERROR_VAL) { // no such course
		cout << "Error7" << endl;
		return;
	}

	if (mismatchCtr == 0) { // all students were added to course
		return;
	}

	int mismatchCtrBackup = mismatchCtr; // save mismatch counter's value
	bool isFirstTime = true; // indicate if found for the first time an ID mismatch

	for (int i = 0; i < numOfStudents; ++i) { // scan results column

		if (idList[i][1] == false) {
			--mismatchCtrBackup; // each time found mismatch, decrement counter

			if (isFirstTime != true) { // not the first ID which could not be added
				cout << idList[i][0]; // print current ID which could not be added
				if (mismatchCtrBackup > 0) { // more mismatches are left to be printed
					cout << ", ";
				}
				continue;
			} // if (isFirstTime != true)
			isFirstTime = false;
			cout << "Error8:["; // print headline only at the first time that an ID mismatch was found
			cout << idList[i][0]; // print current ID which could not be added
			if (mismatchCtrBackup > 0) { // more mismatches are left to be printed
				cout << ", ";
			}

		} // if (idList[i][1] == false)

	} //for (int i = 0; i < numOfStudents; ++i)

	if (mismatchCtr > 0) {
		cout << "]" << endl; // end printing
	}
	return;
}

/***************************************************************************************
* function name: printCourseDetails(Course* courseToPrint)
* The Input: None
* The output: None
* The Function operation: Prints lecturer's details (gets lecturer's details with lecturer's
* 					      get methods) and uses printStudentList method in order to print
* 					      the list of registered students
***************************************************************************************/
void Menu::printCourseDetails(Course* courseToPrint) {

	// print given course's details
	courseToPrint->initStudentsInCourseCurr(); // initialize current node pointer

	cout << "Course:[";
	cout << "No:" << courseToPrint->getCourseNumber() << ", Lecturer:[ID:" << courseToPrint->getLecturerID() << ", name:" << courseToPrint->getLecturerName() << "], "; // print lecturer details
	printStudentList( *(courseToPrint->getStudentsInCourse()) ); // print list of students in course
	cout << "]";

	return;
}

/***************************************************************************************
* function name: printCourseList()
* The Input: None
* The output: None
* The Function operation: Initialize course list's current node pointer and keep scanning
* 						  the course list to its end. For each course in course list,
* 						  prints lecturer's and registered students' id and name using
* 						  printCourseDetails method for course being pointed by
* 						  current node
***************************************************************************************/
void Menu::printCourseList() {

	// scan course list and print each course's details
	m_courseLinkedList->initCurrent(); // initialize current node pointer
	m_courseLinkedList->incrmntCurrent(); // move to next course
	Course* currentCourse = m_courseLinkedList->getCurrentCourse(); // get a pointer to first course on list

	cout << "Courses:" << endl << "{" << endl;
	while (currentCourse != NULL) { // has not reached list's end yet

		// print current course
		printCourseDetails(currentCourse);

		cout << endl;

		m_courseLinkedList->incrmntCurrent(); // move to next course
		currentCourse = m_courseLinkedList->getCurrentCourse(); // get a pointer to first course on list

	} // while (currentCourse != NULL)
	cout << "}" << endl;
	m_studentList->initCurrent(); // done printing, initialize current node pointer's position
	return;
}
